/*
 * @Description: tree工具类
 * @Author: DHL
 * @Date: 2022-01-01 15:18:18
 * @LastEditors: DHL
 * @LastEditTime: 2023-02-15 11:00:11
 */

import { arrayRemoveItem } from './arrayUtils'
import { isEmpty, isNotEmpty } from './isUtils'

/**
 * 序列化Tree中指定属性
 * @param arr 数组
 * @param key 对象属性名
 * @returns
 */
export function treeSerialize<T>(tree: Array<any>, key: string): Array<T> {
  let rtn: any[] = []

  const _serialize = (arr: Array<any>) => {
    for (let item of arr) {
      if (item) {
        const val = item[key]
        if (val && !rtn.includes(val)) {
          rtn.push(val)
        }

        if (item.children && item.children.length > 0) {
          _serialize(item.children)
        }
      }
    }
  }

  _serialize(tree)

  return rtn
}

/**
 * list转tree
 *
 * @param list  list集合
 * @param idKey 主键属性名称
 * @param pidKey  父级属性名称
 * @param pidVal  父级值
 */
export function listToTree(
  list: Nullable<Array<any>>,
  idKey: string,
  pidKey: string,
  pidVal: string
) {
  const result: Array<any> = []
  const itemMap = {}

  if (list && list.length > 0) {
    if (isEmpty(pidVal)) {
      pidVal = ''
    }

    for (const item of list) {
      const id = item[idKey]
      const pid = item[pidKey] || ''

      if (!itemMap[id]) {
        itemMap[id] = {
          children: []
        }
      }

      itemMap[id] = {
        ...item,
        children: itemMap[id]['children']
      }

      const treeItem = itemMap[id]

      if (pid === pidVal) {
        result.push(treeItem)
      } else {
        if (!itemMap[pid]) {
          itemMap[pid] = {
            children: []
          }
        }
        itemMap[pid].children.push(treeItem)
      }
    }
  }
  return result
}

/**
 * list 转 tree
 * 先获取所有的根节点
 * @param list
 * @param idKey
 * @param pidKey
 * @returns
 */
export function listToTreeByRootNodes(
  list: Nullable<Array<any>>,
  idKey: string,
  pidKey: string
): Array<any> {
  let result: Array<any> = []
  if (list && list.length > 0) {
    result = findRootNodes(list, idKey, pidKey)
    result.forEach((rootNode) => {
      const childrenNode = listToTree(list, idKey, pidKey, rootNode[idKey])
      rootNode.children = childrenNode
    })
  }
  return result
}

/**
 * 查询集合中的根节点
 * @param list  list集合
 * @param idKey 主键属性名称
 * @param pidKey  父级属性名称
 * @returns
 */
export function findRootNodes(list: Nullable<Array<any>>, idKey: string, pidKey: string) {
  const map = {}
  const rootIds = new Set()

  if (list && list.length > 0) {
    for (const item of list) {
      map[item[idKey]] = item
      if (!item[pidKey]) {
        // 如果parentOrgId不存在或为空，则认为是根节点
        rootIds.add(item[idKey])
      }
    }
  }

  return Array.from(rootIds).map((id: any) => map[id]) // 返回根节点列表
}

/**
 * 获取父级名称集合
 *
 * @param list  list集合
 * @param idKey 主键属性名称
 * @param pidKey  父级属性名称
 * @param nameKey  获取值属性
 * @param node  父级值
 */
export function getTreeParentName(
  list: Nullable<Array<any>>,
  idKey: string,
  pidKey: string,
  nameKey: string,
  node: any
) {
  let parentNames: Array<string> = []
  const itemMap = {}
  if (list && list.length > 0) {
    // 数组转map
    for (const item of list) {
      const id = item[idKey]
      if (!itemMap[id]) {
        itemMap[id] = item
      }
    }

    // 已知的第一级节点
    if (node && isNotEmpty(node[idKey])) {
      _getParentNode(itemMap[node[idKey]])
    }
  }

  // 递归获取父级节点
  function _getParentNode(node: any) {
    if (node) {
      parentNames.unshift(node[nameKey])
      const pidVal = isEmpty(node[pidKey]) ? '' : node[pidKey]
      if (isNotEmpty(pidVal)) {
        _getParentNode(itemMap[pidVal])
      }
    }
  }

  return parentNames
}

/**
 * 获取tree选中的节点
 * 因为父级被选中后所有子集都会被选中，所以剔除所有父级节点，只保留子集节点
 * @param list 数据集合
 * @param idKey 主键属性名称
 * @param pidKey  父级属性名称
 * @param pidVal  父级值
 * @param checkKeys  选中key集合
 */
export function getTreeCheckKeys(
  tree: Array<any>,
  idKey: string,
  pidKey: string,
  pidVal: string,
  checkKeys: Array<string>
) {
  let isFlag = false

  if (tree && tree.length > 0) {
    for (const node of tree) {
      if (node.checked) {
        checkKeys.push(node[idKey])
      } else {
        arrayRemoveItem(checkKeys, pidVal)
        isFlag = true
      }

      if (node.children && node.children.length > 0) {
        let isRemovePid = getTreeCheckKeys(node.children, idKey, pidKey, node[idKey], checkKeys)

        if (isRemovePid) {
          arrayRemoveItem(checkKeys, pidVal)
        }
      }
    }
  }

  return isFlag
}
